/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include "sdb.h"

#define NR_WP 32

static WP wp_pool[NR_WP] = {};
static WP head_header, free_header;
static WP *head = &head_header, *free_ = &free_header;
static unsigned int NR_USED;

void init_wp_pool() {
  int i;
  for (i = 0; i < NR_WP; i ++) {
    wp_pool[i].NO = i;
    wp_pool[i].next = (i == NR_WP - 1 ? NULL : &wp_pool[i + 1]);
    wp_pool[i].prev = (i == 0 ? free_ : &wp_pool[i - 1]);
  }

  head->next = NULL;
  free_->next = wp_pool;
  NR_USED = 0;
}

/* TODO: Implement the functionality of watchpoint */
WP* new_wp(char* expr_str) {
    // 计算表达式
    bool success;
    uint32_t val = expr(expr_str, &success);
    if (!success) return NULL;
    // 从free_链表中返回第一个空闲的监视点结构
    assert(free_->next!=NULL);
    WP* wp = free_->next;
    wp->next->prev = free_;
    free_->next = wp->next;
    strncpy(wp->expr, expr_str, 127);
    wp->expr[127] = '\0';
    wp->old_val = val;
    wp->val = val;
    // 按顺序加入head中
    WP* cur = head;
    while(cur->next!=NULL && cur->next->NO < wp->NO) {
        cur = cur->next;
    }
    wp->next = cur->next;
    wp->prev = cur;
    cur->next = wp;
    if(wp->next) wp->next->prev = wp;
    NR_USED++;
    return wp;
}

void free_wp(WP *wp) {
    // 从head中取出，并释放空间，目前使用数组，暂时不需要释放
    wp->prev->next = wp->next;
    if(wp->next) wp->next->prev = wp->prev;
    // 将wp放回free，保证按照顺序放回
    WP* cur = free_;
    while(cur->next!=NULL && cur->next->NO < wp->next->NO) {
        cur = cur->next;
    }
    wp->next = cur->next;
    wp->prev = cur;
    cur->next = wp;
    if(wp->next) wp->next->prev = wp;
    NR_USED--;
}

void free_wp_index(int n) {
    // n is the WP->NO
    free_wp(&wp_pool[n]);
}

void list_wp() {
    WP* cur = head;
    printf("Watchpoints used/total = %d/%d\n", NR_USED, NR_WP);
    printf("#ID\tEXPR\tOLD\tCUR\n");
    while(cur->next!=NULL) {
        printf("#%d\t%s\t%u\t%u\n", cur->next->NO, cur->next->expr, cur->next->old_val, cur->next->val);
        cur = cur->next;
    }
}

bool watchpoint_update() {
    WP* cur = head;
    bool success;
    bool changed = false;
    while(cur->next!=NULL) {
        cur->next->old_val = cur->next->val;
        cur->next->val = expr(cur->next->expr, &success);
        cur = cur->next;
        if (cur->old_val!=cur->val) changed = true;
    }
    return changed;
}

